6장. 경계를 연결하는 방법
서비스를 나누면
이제 하나의 프로세스 안에서 함수 호출하던 세상이 끝난다.
그 자리에
네트워크 호출이 들어온다.
마이크로서비스에서 통신은
단순한 기술 선택이 아니라
결합도를 결정하는 설계 선택이다.
함수 호출과 네트워크 호출의 차이
모놀리스에서는 이런 흐름이 자연스럽다.
createOrder()
→ validateUser()
→ processPayment()
모두 같은 프로세스 안에서 실행된다.
하지만 서비스가 나뉘면 이렇게 바뀐다.
Order Service → Payment Service
이제 호출은 네트워크를 건너간다.
그리고 네트워크는 다음과 같은 특성을 가진다.
- 느릴 수 있다
- 실패할 수 있다
- 지연될 수 있다
- 일시적으로 끊길 수 있다
즉, 통신은 항상 실패 가능성을 가진다.
통신 방식은 크게 두 가지다
마이크로서비스 간 통신 방식은 크게 나누면 두 가지다.
- 동기 방식 (요청-응답)
- 비동기 방식 (이벤트 기반)
이 둘은 단순한 기술 차이가 아니라
결합도의 차이다.
동기 통신 — 즉시 응답을 기다리는 방식
예:
- REST API
- gRPC
Order가 Payment를 호출하고
결과를 바로 기다린다.
장점:
- 이해하기 쉽다
- 흐름이 직관적이다
- 트랜잭션처럼 느껴진다
하지만 단점이 있다.
- Payment가 느리면 Order도 느려진다
- Payment가 죽으면 Order도 실패한다
- 호출 체인이 길어질수록 장애 전파가 커진다
동기 통신은
강한 결합을 만든다.
비동기 통신 — 메시지를 남기고 처리하는 방식
예:
- 메시지 큐
- 이벤트 스트림
- Pub/Sub
Order는 결제 요청 이벤트를 발행하고
Payment는 이를 구독하여 처리한다.
Order는 Payment의 응답을 기다리지 않는다.
장점:
- 서비스 간 결합이 약하다
- 한 서비스 장애가 즉시 전파되지 않는다
- 확장성이 좋다
하지만 단점도 있다.
- 흐름이 눈에 보이지 않는다
- 처리 지연이 발생할 수 있다
- 설계가 더 복잡하다
비동기 통신은
결합도를 낮추지만
설계 난이도를 높인다.
언제 동기를 쓰고, 언제 비동기를 쓰는가
이 질문은 기술 질문이 아니라
비즈니스 질문이다.
다음과 같이 생각해볼 수 있다.
즉시 결과가 반드시 필요한 경우
- 결제 승인 여부
- 로그인 인증
→ 동기 통신이 적합하다.
나중에 처리되어도 되는 경우
- 알림 발송
- 통계 집계
- 로그 처리
- 포인트 적립
→ 비동기 통신이 적합하다.
핵심은 이것이다.
반드시 즉시 응답이 필요한가?
그렇지 않다면
비동기를 고려하는 것이 결합도를 낮춘다.
통신 설계에서 반드시 고려해야 할 것
1️⃣ 타임아웃
얼마나 기다릴 것인가?
기다림이 무한이면
시스템은 멈춘다.
2️⃣ 재시도(Retry)
실패하면 다시 시도할 것인가?
하지만 무한 재시도는
더 큰 장애를 만든다.
3️⃣ 멱등성
같은 요청이 여러 번 들어와도
결과가 한 번 처리된 것처럼 유지되어야 한다.
비동기 통신에서는
메시지가 중복될 수 있기 때문이다.
4️⃣ 장애 전파 차단
한 서비스 장애가
연쇄적으로 퍼지지 않도록 설계해야 한다.
동기 체인이 길어질수록
위험은 커진다.
잘못된 통신 설계의 예
다음과 같은 구조는 위험하다.
Order → Payment → User → Notification → Logging
하나라도 느려지면
전체 흐름이 지연된다.
이 구조는
물리적으로는 분리되어 있지만
논리적으로는 하나의 체인이다.
이것은 또 다른 형태의 분산 모놀리스다.
통신은 경계를 드러낸다
데이터 설계는 경계를 지키는 방법이었다.
통신 설계는
그 경계를 어떻게 연결할 것인가에 대한 문제다.
- 동기를 많이 쓰면 경계는 약해지고
- 비동기를 적절히 쓰면 경계는 단단해진다
마이크로서비스에서 통신은
기술 선택이 아니라
아키텍처 선택이다.
이 장의 핵심
서비스를 나누는 순간
통신은 필수가 된다.
- 함수 호출은 네트워크 호출이 되고
- 실패는 예외가 아니라 전제가 되며
- 통신 방식은 결합도를 결정한다
동기와 비동기는
기술 차이가 아니라
설계 철학의 차이다.